import { parse } from "vue/compiler-sfc";
import { globalConfig } from "@gd-accbuild-core/config";
/**
 * vue单文件语法树解析
 */
export const parseSfc = (code) => {
  const res = parse(code, {
    id: "uid",
    filename: "a.vue",
  });
  return {
    template: {
      content: res.descriptor.template ? res.descriptor.template.content : "",
      attrs: res.descriptor.template ? res.descriptor.template.attrs : {},
      ast: res.descriptor.template ? res.descriptor.template.ast : {},
    },
    script: {
      content: res.descriptor.script ? res.descriptor.script.content : "",
      attrs: res.descriptor.script ? res.descriptor.script.attrs : {},
    },
    scriptSetup: {
      content: res.descriptor.scriptSetup ? res.descriptor.scriptSetup.content : "",
      attrs: res.descriptor.scriptSetup ? res.descriptor.scriptSetup.attrs : {},
    },
    styles: res.descriptor.styles.map((el) => ({
      content: el.content,
      attrs: el.attrs,
    })),
  };
};

/**
 * uniapp的template转化为web版的模板;TODO:FIXME:暂且用正则直接修改
 * @param {string} content
 * @returns
 */
export const transUniTemplateToWeb = (content) => {
  content = content.replace(new RegExp("<view", "gm"), "<div");
  content = content.replace(new RegExp("</view", "gm"), "</div");
  return content;
};

function traverseTree(tree, callback = () => {}, result = [], config = { childrenAlias: "children" }) {
  const traverseAllChild = (parentNode, children, result) => {
    let len = children.length;
    let curNode = null;
    for (let i = 0; i < len; i++) {
      curNode = children[i];
      callback(parentNode, curNode, result);
      if (Array.isArray(curNode[config["childrenAlias"]])) {
        traverseAllChild(parentNode, curNode[config["childrenAlias"]], result);
      }
    }
  };
  if (Array.isArray(tree)) {
    tree.forEach((el) => {
      traverseAllChild(null, el.result);
    });
  } else if (Array.isArray(tree[config["childrenAlias"]])) {
    callback(null, [tree], result);
    traverseAllChild(tree, tree[config["childrenAlias"]], result);
  }
}
/**
 * 获取模板中使用easycomp的组件名
 * @param {object} ast
 */
export const getEasycomFromAst = (ast) => {
  const result = [];
  const callback = (parentNode, curNode, result) => {
    if (curNode.type === 1) {
      if (
        curNode.tag.startsWith("gd-") ||
        curNode.tag.startsWith("adv-") ||
        curNode.tag.startsWith("scene-container-") ||
        curNode.tag.startsWith("biz-") ||
        curNode.tag.startsWith("uni-") ||
        curNode.tag.startsWith("el-") ||
        curNode.tag === "DimensionItemContent"
      ) {
        !result.includes(curNode.tag) && result.push(curNode.tag);
      }
    }
  };
  traverseTree(ast, callback, result);
  return result;
};

/**
 * 命名类型转换
 * 支持：Camel小驼峰、Pascal大驼峰、Snake下划线、Kebab中划线
 * @param {string} name 原变量名
 * @param {string} fromNamingRule 原变量命名规则
 * @param {string} toNamingRule 目标变量命名规则
 * */
function transName(name, fromNamingRule, toNamingRule) {
  // 对应命名类型的转换规则
  const NamingRules = {
    // 小驼峰
    Camel: {
      // 把对应命名规则的命名转换成数组状态
      ArrayState: (name) => {
        return name
          .replace(/([A-Z])/g, "-$1")
          .toLowerCase()
          .split("-");
      },
      // 把数组状态转化成对应命名规则的命名
      TransToTargetNaming: (arrayState) => {
        let result = "";
        arrayState.forEach((el, idx) => {
          if (idx === 0) {
            result += el;
          } else {
            result += el.replace(el[0], el[0].toUpperCase());
          }
        });
        return result;
      },
    },
    // 大驼峰
    Pascal: {
      ArrayState: (name) => {
        name = name[0] ? name.replace(name[0], name[0].toUpperCase()) : "";
        return name
          .replace(/([A-Z])/g, "-$1")
          .toLowerCase()
          .split("-")
          .splice(1);
      },
      TransToTargetNaming: (arrayState) => {
        let result = "";
        arrayState.forEach((el, idx) => {
          result += el.replace(el[0], el[0].toUpperCase());
        });
        return result;
      },
    },
    // 下划线
    Snake: {
      ArrayState: (name) => {
        return name.toLowerCase().split("_");
      },
      TransToTargetNaming: (arrayState) => {
        const result = arrayState.join("_");
        return result;
      },
    },
    // 中划线
    Kebab: {
      ArrayState: (name) => {
        return name.toLowerCase().split("-");
      },
      TransToTargetNaming: (arrayState) => {
        const result = arrayState.join("-");
        return result;
      },
    },
  };
  const arrayStateVal = NamingRules[fromNamingRule]["ArrayState"](name);
  const result = NamingRules[toNamingRule]["TransToTargetNaming"](arrayStateVal);
  return result;
}

/**
 * 在script-setup中自动导入easycom组件
 * @param {Array} easycomList
 * @param {string} scriptSetupCode
 * "^gd-(.*)": "@uni_modules/gd-accbuild-core/components/gd-accbuild-ui/gd-ui/gd-$1/gd-$1.vue",
 * "^biz-(.*)": "@uni_modules/gd-accbuild-core/components/gd-accbuild-ui/biz-ui/biz-$1/pc/src/index.vue"
 */
/* 上面例子中请保证各种边缘情况正确，如如果原js代码中没有import语句则，新插入的import语句在第一行等等 */
export const genImportFromEasycomp = (easycomList, scriptSetupCode, targetPlatform = "pc") => {
  const willImport = `const {${easycomList
    .map((el) => transName(el, "Kebab", "Pascal"))
    .join(",")}} = require("injectComp");`;
  const regex = /(import .*?)(["';]\s*[\n\r])/g;

  scriptSetupCode = scriptSetupCode.replace(regex, (match, p1, p2) => {
    return `${p1};\n${willImport}\n${p2}`;
  });
  if (!scriptSetupCode.match(regex)) {
    scriptSetupCode = `${willImport}\n${scriptSetupCode}`;
  }
  return scriptSetupCode;
};
const CompCache = {
  el: null,
  gd: null,
  uni: null,
  biz: null,
};
export default async (code) => {
  const targetPlatform = globalConfig.targetPlatform;
  const parseRes = parseSfc(code);
  let newCode = "";
  //template
  const templateContent = parseRes.template.content;
  const easycomList = getEasycomFromAst(parseRes.template.ast);
  newCode += `<template>${templateContent}</template>`;
  //script
  const scriptContent = parseRes.script.content;
  newCode += `<script${parseRes.script.attrs.lang === "ts" ? " lang=ts" : ""}>${scriptContent}</script>\n`;
  //scriptSetup
  const scriptSetupContent = parseRes.scriptSetup.content;
  let newScriptSetupContent = genImportFromEasycomp(easycomList, scriptSetupContent, targetPlatform);
  newCode += `<script setup${
    parseRes.script.attrs.hasOwnProperty("name") ? ` name=${parseRes.script.attrs.name}` : ""
  }${
    parseRes.script.attrs.hasOwnProperty("lang") ? ` lang=${parseRes.script.attrs.lang}` : ""
  }>${newScriptSetupContent}</script>\n`;
  //styles
  parseRes.styles.forEach((el) => {
    //console.log(el)
    const styleContent = el.content;
    newCode += `<style lang="scss" ${el.attrs.scoped ? " scoped" : ""}${
      el.attrs.module ? (el.attrs.module == true ? " module" : ` module="${el.attrs.module}"`) : ""
    }>${styleContent}</style>\n`;
  });

  if (!CompCache.el) {
    CompCache.el = await (async () => import("element-plus"))();
  }
  const injectComp = {};
  for (let index = 0; index < easycomList.length; index++) {
    const el = easycomList[index];
    if (el === "DimensionItemContent") {
      if (!CompCache["DimensionItemContent"]) {
        CompCache["DimensionItemContent"] = {};
      }
      if (!CompCache["DimensionItemContent"].hasOwnProperty(el)) {
        const r = await (async () =>
          import(`../../../dimension-ui/dimension-item-content/dimension-item-content.vue`))();
        CompCache["DimensionItemContent"] = r.default;
      }
      injectComp["DimensionItemContent"] = CompCache["DimensionItemContent"];
      continue;
    }
    const name = transName(el, "Kebab", "Pascal");
    const prefixName = el.split("-")[0];
    if (prefixName === "gd") {
      if (!CompCache["gd"]) {
        CompCache["gd"] = {};
      }
      if (!CompCache["gd"].hasOwnProperty(el)) {
        const r = await (async () => import(`../../../gd-ui/${el}/${el}.vue`))();
        CompCache["gd"][name] = r.default;
      }
    } else if (prefixName === "scene") {
      if (!CompCache["scene"]) {
        CompCache["scene"] = {};
      }
      if (!CompCache["scene"].hasOwnProperty(el)) {
        const r = await (async () => import(`../../../scene-container/${el}/${el}.vue`))();
        CompCache["scene"][name] = r.default;
      }
    } else if (prefixName === "biz") {
      if (!CompCache["biz"]) {
        CompCache["biz"] = {};
      }
      if (!CompCache["biz"].hasOwnProperty(el)) {
        const r = await (async () => import(`../../../biz-ui/${el}/${el}.vue`))();
        CompCache["biz"][name] = r.default;
      }
    } else if (prefixName === "adv") {
      if (!CompCache["adv"]) {
        CompCache["adv"] = {};
      }
      if (!CompCache["adv"].hasOwnProperty(el)) {
        const r = await (async () => import(`../../../adv-ui/${el}/${el}.vue`))();
        CompCache["adv"][name] = r.default;
      }
    }
    injectComp[name] = CompCache[prefixName][name];
  }
  return {
    source: newCode,
    injectComp,
  };
};
